package com.kingdee.bos.appframework.databinding;

import com.kingdee.bos.ctrl.extendcontrols.KDBizMultiLangArea;
import com.kingdee.bos.ctrl.extendcontrols.KDBizMultiLangBox;
import com.kingdee.bos.ctrl.kdf.table.ICell;
import com.kingdee.bos.ctrl.kdf.table.IColumn;
import com.kingdee.bos.ctrl.kdf.table.IRow;
import com.kingdee.bos.ctrl.kdf.table.KDTSelectManager;
import com.kingdee.bos.ctrl.kdf.table.KDTable;
import com.kingdee.bos.ctrl.kdf.util.style.StyleAttributes;
import com.kingdee.bos.ctrl.swing.KDDatePicker;
import com.kingdee.bos.ctrl.swing.KDFormattedTextField;
import com.kingdee.bos.ctrl.swing.KDNumberTextField;
import com.kingdee.bos.ctrl.swing.KDTimePicker;
import com.kingdee.bos.dao.AbstractObjectValue;
import com.kingdee.bos.dao.IObjectCollection;
import com.kingdee.bos.dao.IObjectValue;
import com.kingdee.bos.framework.agent.IObjectValueAgent;
import com.kingdee.bos.metadata.IMetaDataLoader;
import com.kingdee.bos.metadata.MetaDataLoaderFactory;
import com.kingdee.bos.metadata.entity.EntityObjectInfo;
import com.kingdee.bos.metadata.entity.PropertyInfo;
import com.kingdee.bos.ui.util.UIHelper;
import com.kingdee.util.StringUtils;
import java.awt.Component;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeSupport;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.StringTokenizer;
import org.apache.log4j.Logger;

public class BindingUtility {
	private static HashMap beanAccessorMap;
	private static final Logger logger;

	public static Object getValue(IObjectValue valueObject, Field field) {
		assert (valueObject != null);
		assert (field != null);

		String fieldName = field.getActualName();
		StringTokenizer stFieldName = new StringTokenizer(fieldName, ".");
		Object ret = valueObject;
		while (stFieldName.hasMoreElements()) {
			if (ret == null)
				return null;
			fieldName = stFieldName.nextToken();
			ret = getBeanProperty(ret, fieldName);
		}
		return ret;
	}

	public static void setValue(IObjectValue objectValue, Field field,
			Object propertyValue) {
		if ((objectValue == null) || (field == null)) {
			logger.info("objectValue:" + objectValue + " field:" + field
					+ ",they can not be null.");
			return;
		}

		String fieldName = field.getActualName();
		int lastDotIndex = fieldName.lastIndexOf(".");
		if (lastDotIndex > 0) {
			String actualPropertyName = fieldName.substring(lastDotIndex + 1);
			fieldName = fieldName.substring(0, lastDotIndex);

			Field parentField = new Field(fieldName);
			Object parentProperty = getValue(objectValue, parentField);

			if (parentProperty != null)
				setBeanProperty(parentProperty, actualPropertyName,
						propertyValue);
		} else {
			setBeanProperty(objectValue, fieldName, propertyValue);
		}
	}

	public static Object getComponentValue(Component component,
			String propertyName) {
		return getBeanProperty(component, propertyName);
	}

	public static void loadData(IObjectValue vo, Field field,
			ComponentProperty compProp) {
		assert (vo != null);
		assert (field != null);
		assert (compProp != null);

		Component comp = compProp.getComponent();

		if ((comp instanceof KDBizMultiLangBox)) {
			UIHelper.loadMultiLangFields((KDBizMultiLangBox) compProp
					.getComponent(), vo, field.getName());
		} else if ((comp instanceof KDBizMultiLangArea)) {
			UIHelper.loadMultiLangFields((KDBizMultiLangArea) compProp
					.getComponent(), vo, field.getName());
		} else if ((comp instanceof KDNumberTextField)) {
			Object voProperty = getValue(vo, field);

			((KDNumberTextField) comp).setValue(voProperty);
		} else if ((comp instanceof KDFormattedTextField)) {
			Object voProperty = getValue(vo, field);

			((KDFormattedTextField) comp).setValue(voProperty);
		} else {
			Object voProperty = getValue(vo, field);
			setBeanProperty(compProp.getComponent(),
					compProp.getPropertyName(), voProperty);
		}
	}

	public static void loadDetailData(IObjectValue vo, Field headerField,
			DataComponentMap componentMap, PropertyChangeSupport support) {
		ArrayList detailList = componentMap.getDetailList(headerField);
		if (detailList != null) {
			Field detailField = null;
			int length = detailList.size();
			for (int j = 0; j < length; j++) {
				detailField = (Field) detailList.get(j);
				loadDataToTable(vo, detailField, componentMap, support);
			}
		}
	}

	public static void loadDataToTable(IObjectValue vo, Field detailField,
			DataComponentMap dataCompMap, PropertyChangeSupport support) {
		KDTable table = dataCompMap.getTable(detailField);
		ArrayList fields = dataCompMap.getDetailFields(detailField);

		table.removeRows();

		IObjectCollection detailCollection = (IObjectCollection) getBeanProperty(
				vo, detailField.getActualName());

		if ((detailCollection != null) && (detailCollection.size() >= 0)) {
			table.setUserObject(detailCollection);

			IObjectValue detailObject = null;
			int length = detailCollection.size();
			for (int i = 0; i < length; i++) {
				detailObject = detailCollection.getObject(i);
				table.addRow();
				loadDataToTableLine(i, detailObject, table, fields, dataCompMap);
				if (support != null) {
					PropertyChangeEvent event = new PropertyChangeEvent(table,
							"loadLine", null, new Integer(i));
					support.firePropertyChange(event);
				}
			}
		}
	}

	public static void clearDetailData(IObjectValue vo, Field headerField,
			DataComponentMap componentMap, PropertyChangeSupport support) {
		ArrayList detailList = componentMap.getDetailList(headerField);
		if (detailList != null) {
			Field detailField = null;
			int length = detailList.size();
			for (int j = 0; j < length; j++) {
				detailField = (Field) detailList.get(j);
				clearDataToTable(vo, detailField, componentMap, support);

				clearDetailData(vo, detailField, componentMap, support);
			}
		}
	}

	public static void clearDataToTable(IObjectValue vo, Field detailField,
			DataComponentMap dataCompMap, PropertyChangeSupport support) {
		KDTable table = dataCompMap.getTable(detailField);

		table.removeRows();
	}

	public static void loadDataToTableLine(int row, IObjectValue detailObj,
			KDTable table, ArrayList fields, DataComponentMap dataCompMap,
			PropertyChangeSupport support) {
		loadDataToTableLine(row, detailObj, table, fields, dataCompMap);
		if (support != null) {
			PropertyChangeEvent event = new PropertyChangeEvent(table,
					"loadLine", null, new Integer(row));
			support.firePropertyChange(event);
		}
	}

	public static void loadDataToTableLine(int row, IObjectValue detailObj,
			KDTable table, ArrayList fields, DataComponentMap dataCompMap) {
		assert (detailObj != null);
		assert (table != null);

		if (fields == null) {
			fields = dataCompMap.getDetailFields(dataCompMap.getDetail(table));
		}

		Field colField = null;
		Object colValue = null;

		if (detailObj.getClass() != null) {
			dataCompMap.getDetail(table).setFieldClazz(detailObj.getClass());
		}

		int length = fields.size();
		for (int i = 0; i < length; i++) {
			colField = (Field) fields.get(i);
			colValue = getValue(detailObj, colField);
			ComponentProperty compProp = dataCompMap
					.getComponentProperty(colField);

			setTableCellValue(table, row, compProp.getPropertyName(), colValue);
		}
		setAuthority(detailObj, table.getRow(row));

		table.getRow(row).setUserObject(detailObj);
	}

	public static void setAuthority(IObjectValue detailObj, IRow row) {
		boolean isHasRight = true;
		if (detailObj != null) {
			if (detailObj.containsKey("_V")) {
				Boolean value = (Boolean) detailObj.get("_V");
				if ((value != null) && (!value.booleanValue())) {
					isHasRight = false;
				}
			}
		}
		if (!isHasRight) {
			row.getStyleAttributes().setHided(true);
			row.getStyleAttributes().setLocked(true);
		}
	}

	public static void setTableCellValue(KDTable table, int row,
			String colName, Object colValue) {
		table.getRow(row).getCell(colName).setValue(colValue);
	}

	public static Object getTableCellValue(KDTable table, int row,
			String colName) {
		return table.getRow(row).getCell(colName).getValue();
	}

	public static void storeData(IObjectValue vo, Field field,
			ComponentProperty compProp) {
		assert (vo != null);
		assert (field != null);
		assert (compProp != null);

		int lastIndex = field.getName().lastIndexOf(".");
		Component comp = compProp.getComponent();
		if ((comp instanceof KDBizMultiLangBox)) {
			if (lastIndex > 0) {
				Object obj = getValue(vo, new Field(field.getName().substring(
						0, lastIndex)));
				if ((obj != null) && ((obj instanceof IObjectValue)))
					UIHelper.storeMultiLangFields((KDBizMultiLangBox) compProp
							.getComponent(), (IObjectValue) obj, field
							.getName().substring(lastIndex));
			} else {
				UIHelper.storeMultiLangFields((KDBizMultiLangBox) compProp
						.getComponent(), vo, field.getName());
			}
		} else if ((comp instanceof KDBizMultiLangArea)) {
			if (lastIndex > 0) {
				Object obj = getValue(vo, new Field(field.getName().substring(
						0, lastIndex)));
				if ((obj != null) && ((obj instanceof IObjectValue)))
					UIHelper.storeMultiLangFields((KDBizMultiLangArea) compProp
							.getComponent(), (IObjectValue) obj, field
							.getName().substring(lastIndex));
			} else {
				UIHelper.storeMultiLangFields((KDBizMultiLangArea) compProp
						.getComponent(), vo, field.getName());
			}
		} else if ((comp instanceof KDDatePicker)) {
			Object compProperty = ((KDDatePicker) comp).getValue(field
					.getFieldClazz());

			setValue(vo, field, compProperty);
		} else if ((comp instanceof KDTimePicker)) {
			Object compProperty = ((KDTimePicker) comp).getValue(field
					.getFieldClazz());

			setValue(vo, field, compProperty);
		} else if ((comp instanceof KDNumberTextField)) {
			Object compProperty = ((KDNumberTextField) comp)
					.getNumberValue(field.getFieldClazz());

			setValue(vo, field, compProperty);
		} else if ((comp instanceof KDFormattedTextField)) {
			Object compProperty = ((KDFormattedTextField) comp).getValue(field
					.getFieldClazz());

			setValue(vo, field, compProperty);
		} else {
			Object compProperty = getBeanProperty(compProp.getComponent(),
					compProp.getPropertyName());

			setValue(vo, field, compProperty);
		}
	}

	public static void storeTableToData(KDTable table, Field detailField,
			DataComponentMap dataMap, PropertyChangeSupport support) {
		int rowCount = table.getRowCount();
		if (rowCount > 0)
			for (int i = 0; i < rowCount; i++)
				storeLine(table, i, detailField, dataMap, support);
	}

	private static boolean identityContain(IObjectCollection collection,
			IObjectValue obj) {
		if (collection == null) {
			return false;
		}
		for (int i = collection.size() - 1; i >= 0; i--) {
			if (obj == collection.getObject(i)) {
				return true;
			}
		}
		return false;
	}

	public static void storeLine(KDTable table, int selectedRow,
			Field detailField, DataComponentMap dataMap,
			PropertyChangeSupport support) {
		if ((table == null) || (selectedRow < 0)
				|| (selectedRow > table.getRowCount() - 1)) {
			return;
		}

		if (detailField == null) {
			detailField = dataMap.getDetail(table);
		}
		ArrayList colFields = dataMap.getDetailFields(detailField);
		if ((colFields != null) && (colFields.size() > 0)) {
			IObjectValue rowObj = (IObjectValue) table.getRow(selectedRow)
					.getUserObject();

			if (rowObj == null) {
				try {
					if (IObjectValueAgent.class.isAssignableFrom(detailField
							.getFieldClazz()))
						rowObj = (IObjectValue) detailField.getFieldClazz()
								.getMethod("create", null).invoke(null, null);
					else {
						rowObj = (IObjectValue) detailField.getFieldClazz()
								.newInstance();
					}
					table.getRow(selectedRow).setUserObject(rowObj);
				} catch (Exception e) {
					logger.error("can not having the row object ", e);
					return;
				}
			}

			IObjectCollection collection = (IObjectCollection) table
					.getUserObject();
			if (collection == null) {
				logger.error("not fill detail collection to table user object");
				return;
			}
			if (!identityContain(collection, rowObj)) {
				if (collection.size() < selectedRow) {
					for (int i = 0; i < selectedRow; i++) {
						storeLine(table, i, detailField, dataMap, support);
					}
				}

				collection.insertObject(selectedRow - 1, rowObj);
			}

			Field colField = null;
			Object colValue = null;
			String colName = null;
			int length = colFields.size();
			for (int i = 0; i < length; i++) {
				colField = (Field) colFields.get(i);
				colName = dataMap.getComponentProperty(colField)
						.getPropertyName();

				colValue = getTableCellValue(table, selectedRow, colName);
				setValue(rowObj, colField, colValue);
			}
			if (support != null) {
				PropertyChangeEvent event = new PropertyChangeEvent(table,
						"storeLine", null, new Integer(selectedRow));
				support.firePropertyChange(event);
			}

		}

		ArrayList detailList = dataMap.getDetailList(detailField);
		if (detailList != null) {
			KDTable ddTable = null;

			int j = 0;
			for (int k = detailList.size(); j < k; j++) {
				Field ddetail = (Field) detailList.get(j);
				ddTable = dataMap.getTable(ddetail);
				int selRow = ddTable.getSelectManager().getActiveRowIndex();
				storeLine(ddTable, selRow, ddetail, dataMap, support);
			}
		}
	}

	public static void storeColumn(KDTable table, int selectedRow,
			int columnIndex, Field detailField, DataComponentMap dataMap,
			PropertyChangeSupport support) {
		if ((table == null) || (selectedRow < 0)
				|| (selectedRow > table.getRowCount() - 1)) {
			return;
		}

		if (detailField == null) {
			detailField = dataMap.getDetail(table);
		}
		ArrayList colFields = dataMap.getDetailFields(detailField);
		if ((colFields != null) && (colFields.size() > 0)) {
			IObjectValue rowObj = (IObjectValue) table.getRow(selectedRow)
					.getUserObject();

			if (rowObj == null) {
				try {
					if (IObjectValueAgent.class.isAssignableFrom(detailField
							.getFieldClazz()))
						rowObj = (IObjectValue) detailField.getFieldClazz()
								.getMethod("create", null).invoke(null, null);
					else {
						rowObj = (IObjectValue) detailField.getFieldClazz()
								.newInstance();
					}
					table.getRow(selectedRow).setUserObject(rowObj);
				} catch (Exception e) {
					logger.error("can not having the row object ", e);
					return;
				}
			}

			IObjectCollection collection = (IObjectCollection) table
					.getUserObject();
			if (collection == null) {
				logger.error("not fill detail collection to table user object");
				return;
			}
			if (!identityContain(collection, rowObj)) {
				if (collection.size() < selectedRow) {
					for (int i = 0; i < selectedRow; i++) {
						storeLine(table, i, detailField, dataMap, support);
					}
				}

				collection.insertObject(selectedRow - 1, rowObj);
			}

			Field colField = null;
			Object colValue = null;
			String colName = null;
			String tempColname = null;
			int length = colFields.size();

			for (int i = 0; i < length; i++) {
				colField = (Field) colFields.get(i);
				colName = dataMap.getComponentProperty(colField)
						.getPropertyName();
				tempColname = table.getColumn(columnIndex).getKey();
				if ((tempColname == null) || (!tempColname.equals(colName)))
					continue;
				colValue = getTableCellValue(table, selectedRow, colName);
				setValue(rowObj, colField, colValue);
			}

			if (support != null) {
				PropertyChangeEvent event = new PropertyChangeEvent(table,
						"storeLine", null, new Integer(selectedRow));
				support.firePropertyChange(event);
			}

		}

		ArrayList detailList = dataMap.getDetailList(detailField);
		if (detailList != null) {
			KDTable ddTable = null;

			int j = 0;
			for (int k = detailList.size(); j < k; j++) {
				Field ddetail = (Field) detailList.get(j);
				ddTable = dataMap.getTable(ddetail);
				int selRow = ddTable.getSelectManager().getActiveRowIndex();
				storeLine(ddTable, selRow, ddetail, dataMap, support);
			}
		}
	}

	public static void sortCollection(KDTable table) {
		IObjectCollection collection = (IObjectCollection) table
				.getUserObject();
		if (collection != null) {
			collection.clear();
			for (int r = 0; r < table.getRowCount(); r++) {
				collection.addObject((IObjectValue) table.getRow(r)
						.getUserObject());
			}
		}
	}

	private static boolean isConfigedProperty(Object bean, String propertyName) {
		if ((bean instanceof IObjectValue)) {
			AbstractObjectValue ov = (AbstractObjectValue) bean;
			EntityObjectInfo entity = MetaDataLoaderFactory.getMetaDataLoader(
					null).getEntity(ov.getBOSType());
			PropertyInfo prop = entity.getPropertyByName(propertyName);
			if ((prop != null)
					&& ((prop.isConfigured()) || (!entity.getIsGenerateCode()))) {
				return true;
			}
		}
		return false;
	}

	private static PropertyAccessor getPropertyAccessor(Object bean,
			String propertyName, Object propertyValue) {
		assert (bean != null);
		assert (propertyName != null);

		HashMap pacHashMap = (HashMap) beanAccessorMap.get(bean.getClass());
		if (pacHashMap == null) {
			pacHashMap = new HashMap();
			beanAccessorMap.put(bean.getClass(), pacHashMap);
		}

		PropertyAccessor pac = (PropertyAccessor) pacHashMap.get(propertyName);
		if (pac == null) {
			pac = new PropertyAccessor();
			pacHashMap.put(propertyName, pac);
		}

		Getter getter = pac.getGetter();
		if (getter == null) {
			Method m = null;
			String methodNamePostfix = StringUtils
					.headCharUpperCase(propertyName);
			String methodName = "get" + methodNamePostfix;
			try {
				m = bean.getClass().getMethod(methodName, null);

				if (propertyValue != null) {
					if ((propertyValue.getClass()
							.isAssignableFrom(Boolean.class))
							&& ((!m.getReturnType().isAssignableFrom(
									Boolean.class)) || (!m.getReturnType()
									.equals(Boolean.TYPE)))) {
						m = null;
					}
				}

			} catch (NoSuchMethodException e) {
			}

			if (m == null)
				try {
					methodName = "is" + methodNamePostfix;
					m = bean.getClass().getMethod(methodName, null);
				} catch (NoSuchMethodException e) {
				}
			if (m != null) {
				pac.setGetter(new MethodGetter(m));
			} else if (isConfigedProperty(bean, propertyName)) {
				logger.info("get ConfigedProperty Getter. ClassName:"
						+ bean.getClass() + " propertyName " + propertyName);
				pac.setGetter(new ObjectValueDynamicGetter(propertyName));
			} else {
				logger.error("ClassName:" + bean.getClass()
						+ " hasn't property " + propertyName);
				throw new IllegalArgumentException(
						"No such property ClassName:" + bean.getClass()
								+ " propertyName:" + propertyName);
			}

		}

		Setter setter = pac.getSetter();
		if (setter == null) {
			Method m = null;
			Class retType = pac.getGetter().getReturnType();
			String methodName = "set"
					+ propertyName.substring(0, 1).toUpperCase()
					+ propertyName.substring(1);
			try {
				m = bean.getClass().getMethod(methodName,
						new Class[] { retType });
			} catch (NoSuchMethodException e) {
			}
			if (m != null) {
				pac.setSetter(new MethodSetter(m));
			} else if (isConfigedProperty(bean, propertyName)) {
				logger.info("get ConfigedProperty Setter. ClassName:"
						+ bean.getClass() + " propertyName " + propertyName);
				pac.setSetter(new ObjectValueDynamicSetter(propertyName));
			} else {
				logger.error("ClassName:" + bean.getClass()
						+ " hasn't setter for property " + propertyName);
			}
		}
		return pac;
	}

	public static Object getBeanProperty(Object bean, String propertyName) {
		assert (bean != null);
		assert (propertyName != null);

		PropertyAccessor pac = getPropertyAccessor(bean, propertyName, null);
		try {
			return pac.getGetter().invoke(bean, null);
		} catch (Exception e) {
			logger.error("error", e);
		}
		return null;
	}

	public static void setBeanProperty(Object bean, String propertyName,
			Object propertyValue) {
		PropertyAccessor pac = getPropertyAccessor(bean, propertyName,
				propertyValue);
		try {
			Class paraType = pac.getSetter().getParameterTypes()[0];
			if (propertyValue != null) {
				if (!paraType.equals(propertyValue.getClass())) {
					propertyValue = clazCast(paraType, propertyValue);
				}
				if ((paraType.equals(String.class))
						&& (propertyValue.toString().length() == 0)) {
					propertyValue = null;
				}
			}

			pac.getSetter().invoke(bean, new Object[] { propertyValue });
		} catch (InvocationTargetException ite) {
			logger.error("setBeanProperty occur error in BindingUtility! ", ite
					.getCause());
		} catch (Exception e) {
			logger.error("setBeanProperty occur error in BindingUtility! ", e);
		}
	}

	static Object clazCast(Class paraType, Object value) {
		if (paraType.equals(String.class))
			return String.valueOf(value);
		if (paraType.equals(Integer.class))
			return Integer.valueOf(value.toString());
		if (paraType.equals(Long.class))
			return Long.valueOf(value.toString());
		if (paraType.equals(Short.class))
			return Short.valueOf(value.toString());
		if (paraType.equals(Double.class))
			return Double.valueOf(value.toString());
		if (paraType.equals(BigDecimal.class))
			return new BigDecimal(value.toString());
		if (paraType.equals(Integer.TYPE))
			return Integer.valueOf(value.toString());
		if (paraType.equals(Long.TYPE))
			return Long.valueOf(value.toString());
		if (paraType.equals(Float.TYPE))
			return Float.valueOf(value.toString());
		if (paraType.equals(Double.TYPE))
			return Double.valueOf(value.toString());
		if (paraType.equals(Short.TYPE))
			return Short.valueOf(value.toString());
		if ((paraType.equals(java.sql.Date.class))
				&& ((value instanceof java.util.Date)))
			return new java.sql.Date(((java.util.Date) value).getTime());
		if ((paraType.equals(Time.class))
				&& ((value instanceof java.util.Date)))
			return new Time(((java.util.Date) value).getTime());
		if ((paraType.equals(Timestamp.class))
				&& ((value instanceof java.util.Date))) {
			return new Timestamp(((java.util.Date) value).getTime());
		}
		return value;
	}

	public static void loadDetailData(IObjectCollection vo, Field headerField,
			DataComponentMap componentMap, PropertyChangeSupport support) {
		ArrayList detailList = componentMap.getDetailList(headerField);
		if (detailList != null) {
			Field detailField = null;
			int length = detailList.size();
			for (int j = 0; j < length; j++) {
				detailField = (Field) detailList.get(j);
				loadDataToTable(vo, detailField, componentMap, support);
			}
		}
	}

	public static void loadDataToTable(IObjectCollection vo, Field detailField,
			DataComponentMap dataCompMap, PropertyChangeSupport support) {
		KDTable table = dataCompMap.getTable(detailField);
		ArrayList fields = dataCompMap.getDetailFields(detailField);

		table.removeRows();

		IObjectCollection detailCollection = vo;
		if ((detailCollection != null) && (detailCollection.size() >= 0)) {
			table.setUserObject(detailCollection);

			IObjectValue detailObject = null;
			int length = detailCollection.size();
			for (int i = 0; i < length; i++) {
				detailObject = detailCollection.getObject(i);
				table.addRow();
				loadDataToTableLine(i, detailObject, table, fields, dataCompMap);
				if (support == null)
					continue;
				PropertyChangeEvent event = new PropertyChangeEvent(table,
						"loadLine", null, new Integer(i));
				support.firePropertyChange(event);
			}
		}
	}

	static {
		beanAccessorMap = new HashMap();

		logger = Logger.getLogger(BindingUtility.class);
	}
}